#include "LayerListWidget.h"
#include "graphicslayersetdialog.h"
#include "ItemModels/LayerSetModel.h"
#include "Widgets/ModelItemCheckBoxDelegate.h"

#include <QComboBox>
#include <QCheckBox>
#include <QDoubleSpinBox>
#include <QFont>
#include <QFontMetrics>
#include <QHeaderView>
#include <QIcon>
#include <QItemEditorFactory>
#include <QPixmap>
#include <QPainter>
#include <QSlider>
#include <QSortFilterProxyModel>
#include <QTableView>
#include <QToolBar>
#include <QVBoxLayout>

// FIXME: X{Right|Left}Y{Up|Down} is actually for the graphics-view transform
// We need a user control "view from top/bottom", Altium's "Flip board" flip X
// And swap all layers with their logical pair.
LayerListWidget::LayerListWidget(QWidget *parent):
    QWidget(parent),
    m_view(new QTableView),
    m_model(nullptr),
    m_proxyModel(new QSortFilterProxyModel)
{
    m_orientationComboBox = new QComboBox();
    m_orientationComboBox->addItem(orientationIcon(LeGraphicsView::XRightYUp),
                                   orientationText(LeGraphicsView::XRightYUp),
                                   orientationData(LeGraphicsView::XRightYUp));
    m_orientationComboBox->addItem(orientationIcon(LeGraphicsView::XRightYDown),
                                   orientationText(LeGraphicsView::XRightYDown),
                                   orientationData(LeGraphicsView::XRightYDown));
    m_orientationComboBox->addItem(orientationIcon(LeGraphicsView::XLeftYUp),
                                   orientationText(LeGraphicsView::XLeftYUp),
                                   orientationData(LeGraphicsView::XLeftYUp));
    m_orientationComboBox->addItem(orientationIcon(LeGraphicsView::XLeftYDown),
                                   orientationText(LeGraphicsView::XLeftYDown),
                                   orientationData(LeGraphicsView::XLeftYDown));
    m_orientationComboBox->setCurrentIndex(0);
    connect(m_orientationComboBox, &QComboBox::currentTextChanged,
            this, [this](const QString &)
    {
        emit graphicsViewOrientationChanged(m_orientationComboBox->currentData().value<LeGraphicsView::Orientation>());
    });

    m_rotationSpinBox = new QDoubleSpinBox();
    m_rotationSpinBox->setSingleStep(1.0);
    m_rotationSpinBox->setDecimals(1);
    m_rotationSpinBox->setMinimum(0.0);
    m_rotationSpinBox->setMaximum(359.9);
    m_rotationSpinBox->setValue(0.0);
    connect(m_rotationSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
            this, &LayerListWidget::graphicsViewRotationChanged);


    // FIXME: Need a enableTransparentLayers(bool)
    m_opacitySlider = new QSlider();
    m_opacitySlider->setEnabled(false);
    m_opacitySlider->setMinimum(0);
    m_opacitySlider->setMaximum(100);
    m_opacitySlider->setValue(50);
    m_opacitySlider->setOrientation(Qt::Horizontal);
//    connect(m_opacitySlider, &QSlider::valueChanged,
//            m_model->setAllLayerOpacity(value/100.0);)

    m_topToolBar = new QToolBar();
    m_topToolBar->addWidget(m_orientationComboBox);
    m_topToolBar->addWidget(m_rotationSpinBox);
    m_topToolBar->addAction(QIcon::fromTheme("settings-configure"), "Layer-set...",
                            this, &LayerListWidget::configureLayerSetRequested);

    m_bottomToolBar = new QToolBar();
    m_bottomToolBar->addAction(QIcon::fromTheme("arrow-up-double"), "Move to top",
                               this, &LayerListWidget::moveCurrentLayerToTop);
    m_bottomToolBar->addAction(QIcon::fromTheme("arrow-up"), "Move up",
                               this, &LayerListWidget::moveCurrentLayerUp);
    m_bottomToolBar->addAction(QIcon::fromTheme("arrow-down"), "Move down",
                               this, &LayerListWidget::moveCurrentLayerDown);
    m_bottomToolBar->addAction(QIcon::fromTheme("arrow-down-double"), "Move to bottom",
                               this, &LayerListWidget::moveCurrentLayerToBottom);
    m_bottomToolBar->addSeparator();

    setLayout(new QVBoxLayout);
    m_topToolBar->layout()->setMargin(0);
    layout()->addWidget(m_topToolBar);
    layout()->addWidget(m_view);
    m_bottomToolBar->layout()->setMargin(0);
    layout()->addWidget(m_bottomToolBar);
    layout()->setMargin(1);
}

LayerListWidget::~LayerListWidget()
{
    delete m_proxyModel;
}

void LayerListWidget::setLayerSetModel(LayerSetModel *model)
{
    m_view->selectionModel()->disconnect(this);

    m_model = model;

    m_proxyModel->setSourceModel(model);
    m_proxyModel->setFilterKeyColumn(LayerSetModel::EnabledColumn);
    m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
    m_proxyModel->setFilterFixedString("true");

    m_view->setModel(m_proxyModel);

    connect(m_view->selectionModel(), &QItemSelectionModel::currentRowChanged,
            this, [this](const QModelIndex &current, const QModelIndex &previous) {
        emit currentLayerChanged(m_proxyModel->mapToSource(current));
    });

    m_view->setAlternatingRowColors(true);
    m_view->setSelectionBehavior(QTableView::SelectRows);
    m_view->setSelectionMode(QTableView::SingleSelection);

    m_view->hideColumn(LayerSetModel::EnabledColumn);
    m_view->hideColumn(LayerSetModel::PadVisibleColumn);
    m_view->hideColumn(LayerSetModel::PadBrushColumn);
    m_view->hideColumn(LayerSetModel::PinVisibleColumn);
    m_view->hideColumn(LayerSetModel::PinBrushColumn);
    m_view->hideColumn(LayerSetModel::FiducialVisibleColumn);
    m_view->hideColumn(LayerSetModel::FiducialBrushColumn);
    m_view->hideColumn(LayerSetModel::HoleVisibleColumn);
    m_view->hideColumn(LayerSetModel::HoleBrushColumn);
    m_view->hideColumn(LayerSetModel::SlotVisibleColumn);
    m_view->hideColumn(LayerSetModel::SlotBrushColumn);
    m_view->hideColumn(LayerSetModel::FillVisibleColumn);
    m_view->hideColumn(LayerSetModel::FillBrushColumn);
    m_view->hideColumn(LayerSetModel::TraceVisibleColumn);
    m_view->hideColumn(LayerSetModel::TraceBrushColumn);
    m_view->hideColumn(LayerSetModel::TextVisibleColumn);
    m_view->hideColumn(LayerSetModel::TextBrushColumn);

    m_view->setItemDelegateForColumn(LayerSetModel::VisibleColumn,
                                     new ModelItemCheckBoxDelegate(m_view));

    auto header = m_view->horizontalHeader();
    header->setSectionResizeMode(QHeaderView::ResizeToContents);
    header->setStretchLastSection(true);
    header->hide();
}

void LayerListWidget::setGraphicsViewOrientation(LeGraphicsView::Orientation orientation)
{
    switch (orientation)
    {
        case LeGraphicsView::XRightYUp:
            m_orientationComboBox->setCurrentIndex(0);
            break;
        case LeGraphicsView::XRightYDown:
            m_orientationComboBox->setCurrentIndex(1);;
            break;
        case LeGraphicsView::XLeftYUp:
            m_orientationComboBox->setCurrentIndex(2);
            break;
        case LeGraphicsView::XLeftYDown:
            m_orientationComboBox->setCurrentIndex(3);
            break;
    }
}

void LayerListWidget::setGraphicsViewRotation(qreal angle)
{
    m_rotationSpinBox->setValue(angle);
}

void LayerListWidget::setCurrentLayer(const QModelIndex &index)
{
    m_view->setCurrentIndex(m_proxyModel->mapFromSource(index));
}

void LayerListWidget::moveCurrentLayerUp()
{
    auto currentIndex = m_view->currentIndex();
    currentIndex = m_proxyModel->mapToSource(currentIndex);
    currentIndex = m_model->moveUp(currentIndex);
    currentIndex = m_proxyModel->mapFromSource(currentIndex);
    m_view->setCurrentIndex(currentIndex);
}

void LayerListWidget::moveCurrentLayerToTop()
{
    auto currentIndex = m_view->currentIndex();
    currentIndex = m_proxyModel->mapToSource(currentIndex);
    currentIndex = m_model->moveToTop(currentIndex);
    currentIndex = m_proxyModel->mapFromSource(currentIndex);
    m_view->setCurrentIndex(currentIndex);
}

void LayerListWidget::moveCurrentLayerDown()
{
    auto currentIndex = m_view->currentIndex();
    currentIndex = m_proxyModel->mapToSource(currentIndex);
    currentIndex = m_model->moveDown(currentIndex);
    currentIndex = m_proxyModel->mapFromSource(currentIndex);
    m_view->setCurrentIndex(currentIndex);
}

void LayerListWidget::moveCurrentLayerToBottom()
{
    auto currentIndex = m_view->currentIndex();
    currentIndex = m_proxyModel->mapToSource(currentIndex);
    currentIndex = m_model->moveToBottom(currentIndex);
    currentIndex = m_proxyModel->mapFromSource(currentIndex);
    m_view->setCurrentIndex(currentIndex);
}

// Icon for user, so YUp means no y-flip of the icon
QIcon LayerListWidget::orientationIcon(LeGraphicsView::Orientation orientation)
{
    QPixmap pixmap(64, 64);
    QPainter painter(&pixmap);
    painter.fillRect(pixmap.rect(), Qt::white);
    QString text("AB");
    QFont font;
    font.setPixelSize(40);
    painter.setBrush(Qt::black);
    painter.setFont(font);
    painter.drawText(pixmap.rect(), text, Qt::AlignVCenter | Qt::AlignHCenter);

    QTransform transform;
    switch (orientation)
    {
        case LeGraphicsView::XRightYUp: // No Flip
            transform = QTransform(1, 0, 0,
                                   0, 1, 0,
                                   0, 0, 1);
            break;
        case LeGraphicsView::XRightYDown: // Flip Y
            transform = QTransform(1,  0, 0,
                                   0, -1, 0,
                                   0,  0, 1);
            break;
        case LeGraphicsView::XLeftYUp: // Flip X
            transform = QTransform(-1, 0, 0,
                                    0, 1, 0,
                                    0, 0, 1);
            break;
        case LeGraphicsView::XLeftYDown: // Flip X and Y
            transform = QTransform(-1,  0, 0,
                                    0, -1, 0,
                                    0,  0, 1);
            break;
    }
    return QIcon(pixmap.transformed(transform));
}

QString LayerListWidget::orientationText(LeGraphicsView::Orientation orientation)
{
    switch (orientation)
    {
        case LeGraphicsView::XRightYUp:
            return "X Right, Y Up";
        case LeGraphicsView::XRightYDown:
            return "X Right, Y Down";
        case LeGraphicsView::XLeftYUp:
            return "X Left, Y Up";
        case LeGraphicsView::XLeftYDown:
            return "X Left, Y Down";
    }
    return "";
}

QVariant LayerListWidget::orientationData(LeGraphicsView::Orientation orientation)
{
    return QVariant::fromValue<LeGraphicsView::Orientation>(orientation);
}
